From: Philippe Antoine Date: Thu, 30 Oct 2025 10:18:15 +0000 (+0100) Subject: [PATCH] output/jsonbuilder: helper function SCJbSetPrintAsciiString X-Git-Tag: archive/raspbian/1%7.0.10-1+rpi1+deb13u2^2~2 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https://%22Programmet/%22http:/www.example.com/cgi/%22https:/%22Programmet?a=commitdiff_plain;h=026cf5a2a1d660251e388dd5fa3491c72315adb0;p=suricata.git [PATCH] output/jsonbuilder: helper function SCJbSetPrintAsciiString To replace C PrintStringsToBuffer and avoid a stack alloc + copy Ticket: 8004 (cherry picked from commit 7447651fa0956ff4ce55283a51b4a9494ec8cc6a) Origin: upstream, https://github.com/OISF/suricata/commit/5abf9b81e78476f49ab074f3a74b5840747cd069.patch Bug: https://redmine.openinfosecfoundation.org/issues/8004 Subject: Upstream fix for CVE-2025-64331 Gbp-Pq: Name CVE-2025-64331.patch --- diff --git a/rust/dist/rust-bindings.h b/rust/dist/rust-bindings.h index 6f3cd54e..47cd9ae9 100644 --- a/rust/dist/rust-bindings.h +++ b/rust/dist/rust-bindings.h @@ -4898,6 +4898,11 @@ bool jb_set_string_from_bytes(struct JsonBuilder *js, const uint8_t *bytes, uint32_t len); +bool SCJbSetPrintAsciiString(struct JsonBuilder *js, + const char *key, + const uint8_t *bytes, + uint32_t len); + bool jb_set_base64(struct JsonBuilder *js, const char *key, const uint8_t *bytes, diff --git a/rust/src/jsonbuilder.rs b/rust/src/jsonbuilder.rs index 227ee40f..5418275b 100644 --- a/rust/src/jsonbuilder.rs +++ b/rust/src/jsonbuilder.rs @@ -527,6 +527,52 @@ impl JsonBuilder { } } + /// Set a key with a string value taking only ascii-printable bytes. + /// Non-printable characters are replaced by a dot `.`, except + /// CR and LF which are escaped the regular json way \r and \n + pub fn set_print_ascii(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> { + match self.current_state() { + State::ObjectNth => { + self.push(',')?; + } + State::ObjectFirst => { + self.set_state(State::ObjectNth); + } + _ => { + debug_validate_fail!("invalid state"); + return Err(JsonError::InvalidState); + } + } + self.push('"')?; + self.push_str(key)?; + self.push_str("\":\"")?; + for &x in val.iter() { + match x { + b'\r' => { + self.push_str("\\r")?; + } + b'\n'=> { + self.push_str("\\n")?; + } + b'"'=> { + self.push_str("\\\"")?; + } + b'\\'=> { + self.push_str("\\\\")?; + } + _ => { + if !x.is_ascii() || x.is_ascii_control() { + self.push('.')?; + } else { + self.push(x as char)?; + } + } + } + } + self.push('"')?; + Ok(self) + } + /// Set a key and a string value (from bytes) on an object, with a limited size pub fn set_string_from_bytes_limited(&mut self, key: &str, val: &[u8], limit: usize) -> Result<&mut Self, JsonError> { let mut valtrunc = Vec::new(); @@ -884,6 +930,20 @@ pub unsafe extern "C" fn jb_set_string_from_bytes( return false; } +#[no_mangle] +pub unsafe extern "C" fn SCJbSetPrintAsciiString( + js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32, +) -> bool { + if bytes.is_null() || len == 0 { + return false; + } + if let Ok(key) = CStr::from_ptr(key).to_str() { + let val = std::slice::from_raw_parts(bytes, len as usize); + return js.set_print_ascii(key, val).is_ok(); + } + return false; +} + #[no_mangle] pub unsafe extern "C" fn jb_set_base64( js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32, diff --git a/src/output-json-alert.c b/src/output-json-alert.c index 72127e5d..495b8285 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -452,13 +452,7 @@ static void AlertAddPayload(AlertJsonOutputCtx *json_output_ctx, JsonBuilder *js } if (json_output_ctx->flags & LOG_JSON_PAYLOAD) { - uint8_t printable_buf[p->payload_len + 1]; - uint32_t offset = 0; - PrintStringsToBuffer(printable_buf, &offset, - p->payload_len + 1, - p->payload, p->payload_len); - printable_buf[p->payload_len] = '\0'; - jb_set_string(js, "payload_printable", (char *)printable_buf); + SCJbSetPrintAsciiString(js, "payload_printable", p->payload, p->payload_len); } } @@ -764,11 +758,8 @@ static bool AlertJsonStreamData(const AlertJsonOutputCtx *json_output_ctx, JsonA } if (json_output_ctx->flags & LOG_JSON_PAYLOAD) { - uint8_t printable_buf[cbd.payload->offset + 1]; - uint32_t offset = 0; - PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer, - cbd.payload->offset); - jb_set_string(jb, "payload_printable", (char *)printable_buf); + SCJbSetPrintAsciiString( + jb, "payload_printable", cbd.payload->buffer, cbd.payload->offset); } return true; } diff --git a/src/output-json-frame.c b/src/output-json-frame.c index 69e74a7e..8d53f30e 100644 --- a/src/output-json-frame.c +++ b/src/output-json-frame.c @@ -202,11 +202,7 @@ static void FrameAddPayloadTCP(Flow *f, const TcpSession *ssn, const TcpStream * if (cbd.payload->offset) { jb_set_base64(jb, "payload", cbd.payload->buffer, cbd.payload->offset); - uint8_t printable_buf[cbd.payload->offset + 1]; - uint32_t offset = 0; - PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer, - cbd.payload->offset); - jb_set_string(jb, "payload_printable", (char *)printable_buf); + SCJbSetPrintAsciiString(jb, "payload_printable", cbd.payload->buffer, cbd.payload->offset); jb_set_bool(jb, "complete", complete); } } @@ -235,11 +231,7 @@ static void FrameAddPayloadUDP(JsonBuilder *js, const Packet *p, const Frame *fr const uint32_t log_data_len = MIN(data_len, 256); jb_set_base64(js, "payload", data, log_data_len); - uint8_t printable_buf[log_data_len + 1]; - uint32_t o = 0; - PrintStringsToBuffer(printable_buf, &o, log_data_len + 1, data, log_data_len); - printable_buf[log_data_len] = '\0'; - jb_set_string(js, "payload_printable", (char *)printable_buf); + SCJbSetPrintAsciiString(js, "payload_printable", data, log_data_len); #if 0 char pretty_buf[data_len * 4 + 1]; pretty_buf[0] = '\0'; diff --git a/src/output-json-http.c b/src/output-json-http.c index c58c32fd..87191caa 100644 --- a/src/output-json-http.c +++ b/src/output-json-http.c @@ -366,7 +366,6 @@ static void EveHttpLogJSONHeaders( static void BodyPrintableBuffer(JsonBuilder *js, HtpBody *body, const char *key) { if (body->sb != NULL && body->sb->region.buf != NULL) { - uint32_t offset = 0; const uint8_t *body_data; uint32_t body_data_len; uint64_t body_offset; @@ -376,13 +375,7 @@ static void BodyPrintableBuffer(JsonBuilder *js, HtpBody *body, const char *key) return; } - uint8_t printable_buf[body_data_len + 1]; - PrintStringsToBuffer(printable_buf, &offset, - sizeof(printable_buf), - body_data, body_data_len); - if (offset > 0) { - jb_set_string(js, key, (char *)printable_buf); - } + SCJbSetPrintAsciiString(js, key, body_data, body_data_len); } } diff --git a/src/output-json.c b/src/output-json.c index e25ee516..6f804840 100644 --- a/src/output-json.c +++ b/src/output-json.c @@ -210,22 +210,10 @@ static void EveAddPacketVars(const Packet *p, JsonBuilder *js_vars) PrintStringsToBuffer(keybuf, &offset, sizeof(keybuf), pv->key, pv->key_len); - uint32_t len = pv->value_len; - uint8_t printable_buf[len + 1]; - offset = 0; - PrintStringsToBuffer(printable_buf, &offset, - sizeof(printable_buf), - pv->value, pv->value_len); - jb_set_string(js_vars, (char *)keybuf, (char *)printable_buf); + SCJbSetPrintAsciiString(js_vars, (char *)keybuf, pv->value, pv->value_len); } else { const char *varname = VarNameStoreLookupById(pv->id, VAR_TYPE_PKT_VAR); - uint32_t len = pv->value_len; - uint8_t printable_buf[len + 1]; - uint32_t offset = 0; - PrintStringsToBuffer(printable_buf, &offset, - sizeof(printable_buf), - pv->value, pv->value_len); - jb_set_string(js_vars, varname, (char *)printable_buf); + SCJbSetPrintAsciiString(js_vars, varname, pv->value, pv->value_len); } jb_close(js_vars); } @@ -276,15 +264,9 @@ static void EveAddFlowVars(const Flow *f, JsonBuilder *js_root, JsonBuilder **js break; } - uint32_t len = fv->data.fv_str.value_len; - uint8_t printable_buf[len + 1]; - uint32_t offset = 0; - PrintStringsToBuffer(printable_buf, &offset, - sizeof(printable_buf), - fv->data.fv_str.value, fv->data.fv_str.value_len); - jb_start_object(js_flowvars); - jb_set_string(js_flowvars, varname, (char *)printable_buf); + SCJbSetPrintAsciiString( + js_flowvars, varname, fv->data.fv_str.value, fv->data.fv_str.value_len); jb_close(js_flowvars); } } else if (fv->datatype == FLOWVAR_TYPE_STR && fv->key != NULL) { @@ -300,15 +282,9 @@ static void EveAddFlowVars(const Flow *f, JsonBuilder *js_root, JsonBuilder **js sizeof(keybuf), fv->key, fv->keylen); - uint32_t len = fv->data.fv_str.value_len; - uint8_t printable_buf[len + 1]; - offset = 0; - PrintStringsToBuffer(printable_buf, &offset, - sizeof(printable_buf), - fv->data.fv_str.value, fv->data.fv_str.value_len); - jb_start_object(js_flowvars); - jb_set_string(js_flowvars, (const char *)keybuf, (char *)printable_buf); + SCJbSetPrintAsciiString(js_flowvars, (const char *)keybuf, fv->data.fv_str.value, + fv->data.fv_str.value_len); jb_close(js_flowvars); } else if (fv->datatype == FLOWVAR_TYPE_INT) { const char *varname = VarNameStoreLookupById(fv->idx,